#include <common.h>
#include <asm.h>
#include <os/bios.h>
#include <os/task.h>
#include <os/string.h>
#include <os/loader.h>
#include <type.h>

#define TASK5 1
#define TASKNUM_LOC 0x502001fa
#define VERSION_BUF 50

int version = 2; // version must between 0 and 9
char buf[VERSION_BUF];

// Task info array
task_info_t tasks[TASK_MAXNUM];

static int bss_check(void)
{
    for (int i = 0; i < VERSION_BUF; ++i)
    {
        if (buf[i] != 0)
        {
            return 0;
        }
    }
    return 1;
}

static void init_bios(void)
{
    volatile long (*(*jmptab))() = (volatile long (*(*))())BIOS_JMPTAB_BASE;

    jmptab[CONSOLE_PUTSTR] = (long (*)())port_write;
    jmptab[CONSOLE_PUTCHAR] = (long (*)())port_write_ch;
    jmptab[CONSOLE_GETCHAR] = (long (*)())port_read_ch;
    jmptab[SD_READ] = (long (*)())sd_read;
}

static void init_task_info(void)
{
    // TODO: [p1-task4] Init 'tasks' array via reading app-info sector
    // NOTE: You need to get some related arguments from bootblock first
    char *tasknum = TASKNUM_LOC;
    for (int i = 1; i <= *tasknum; i++)
    {
        memcpy(tasks[i].name, (char *)(0x502001f0 - i * 0x10), 8);
        memcpy(&tasks[i].start_block, (short *)(0x502001f0 - i * 0x10 + 0x8), 2);
        memcpy(&tasks[i].num_block, (short *)(0x502001f0 - i * 0x10 + 0xa), 2);
        memcpy(&tasks[i].offset, (int *)(0x502001f0 - i * 0x10 + 0xc), 4);
    }
}

int main(void)
{

    // Check whether .bss section is set to zero
    int check = bss_check();

    // Init jump table provided by BIOS ()
    init_bios();

    // Init task information ()
    init_task_info();

    // Output 'Hello OS!', bss check result and OS version
    char output_str[] = "bss check: _ version: _\n\r";
    char output_val[2] = {0};
    int i, output_val_pos = 0;
    output_val[0] = check ? 't' : 'f';
    output_val[1] = version + '0';
    for (i = 0; i < sizeof(output_str); ++i)
    {
        buf[i] = output_str[i];
        if (buf[i] == '_')
        {
            buf[i] = output_val[output_val_pos++];
        }
    }

    bios_putstr("Hello OS!\n\r");
    bios_putstr(buf);

    // TODO: Load tasks by either task id [p1-task3] or task name [p1-task4],
    //   and then execute them.

#ifdef TASK3

    while (1)
    {
        int p = ' ';
        int inputnum;
        while (p != 'q')
        {
            p = bios_getchar();
            if (p != 'q' && p != -1)
            {
                bios_putchar(p);
                inputnum = p;
            }
        }
        bios_putstr("The task is ");
        bios_putchar(inputnum);
        bios_putchar('\n');
        unsigned mem_address;
        mem_address = load_task_img(inputnum - '0');

        if (mem_address == 0)
        {

            bios_putstr("wrong task! ");
            bios_putchar('\n');
            continue;
        }
        else
        {
            void (*func)(void) = (void *)mem_address;
            func();
        }
    }
#elif TASK4
    while (1)
    {
        int p = ' ';
        unsigned mem_address;
        char inputnum[8];
        int loc = 0;
        while (p != 'q')
        {
            p = bios_getchar();
            if (p != 'q' && p != -1)
            {
                bios_putchar(p);
                inputnum[loc++] = p;
            }
        }
        inputnum[loc] = '\0';
        bios_putstr("The task is ");
        bios_putstr(inputnum);
        bios_putchar('\n');

        mem_address = load_task_img(inputnum);
        if (mem_address == 0)
        {

            bios_putstr("wrong task! ");
            bios_putchar('\n');
            continue;
        }
        else
        {
            void (*func)(void) = (void *)mem_address;
            func();
        }
    }
#elif TASK5

    unsigned mem_address;
    int loc = 0x502001f9;
    char pdc;
    while (1)
    {
        memcpy(&pdc, (char *)loc, 1);
        bios_putchar(pdc);
        bios_putchar('\n');
        loc--;
        if (pdc == 'q')
            break;
        else
        {
            mem_address = load_task_img(pdc - '0');

            void (*func)(void) = (void *)mem_address;
            func();
        }
    }
#endif

    // Infinite while loop, where CPU stays in a low-power state (QAQQQQQQQQQQQ)
    while (1)
    {
        asm volatile("wfi");
    }
    return 0;
}
